package com.bottle.web;

import com.bottle.enity.User;
import com.bottle.event.ChatRecordEvent;
import com.bottle.event.MessageEvent;
import com.bottle.mode.BridgeInfo;
import com.bottle.mode.ReJson;
import com.bottle.mode.RequestMessage;
import com.bottle.mode.UserInfo;
import com.bottle.service.CharService;
import com.bottle.service.UserService;
import com.bottle.utils.SpringUtil;
import com.google.common.base.Optional;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Multimap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;

/**
 * @author: chenyuncong
 * @Date: 2019/11/28 16:07
 */


@Slf4j
@RestController
public class PollingController implements CharService {
    //guava中的Multimap，多值map,对map的增强，一个key可以保持多个value
    @Autowired
    private Multimap<String, DeferredResult<Object>> multimap;

    //用户缓存，
    @Autowired
    private LoadingCache<String, Optional<UserInfo>> loadingCache;

    //桥梁对象 保存用户关系
    @Autowired
    private LoadingCache<String, Optional<BridgeInfo>> bridgeInfoCache;

    //所有用户的加密id
    @Autowired
    private ConcurrentHashMap<String, String> userIdCache;

    @Autowired
    private UserService userService;

    @PostConstruct
    public void init() {
        //触发所有userid加进缓存
        userService.loadAllUserIdToCache();
    }

    //模拟长轮询
    @Override
    @RequestMapping(value = "/watch/{namespace}")
    public DeferredResult<Object> watch(@PathVariable("namespace") String namespace) {
        DeferredResult<Object> deferredResult = new DeferredResult<>();
        log.info("Request received");
        if (!userIdCache.containsKey(namespace)) {
            deferredResult.setResult(new ReJson<>(200, "", "没有对应用户"));
            return deferredResult;
        }
        String userId = userIdCache.get(namespace);
        //当deferredResult完成时（不论是超时还是异常还是正常完成），移除watchRequests中相应的watch key
        deferredResult.onCompletion(() -> {
            log.info("remove key:" + userId);
            multimap.remove(userId, deferredResult);
        });
        multimap.put(userId, deferredResult);
        log.info("Servlet thread released");
        return deferredResult;

    }


    //接收客户端发送过来的消息
    @Override
    @PostMapping("receive")
    public ReJson receive(RequestMessage request) throws ExecutionException {

        //拿到用户加密id，去获取用户对象，拿到用户id
        String userId = userIdCache.get(request.getEncrUserId());
        if (StringUtils.isEmpty(userId)) { //不存在这个用户
            return new ReJson();
        }
        UserInfo userInfo = loadingCache.get(userId).get(); //用户信息对象，里面用户对应的所有桥梁关系id
        if (StringUtils.isEmpty(userInfo.getUserId())) { //空对象不存在的用户
            return new ReJson();
        }

        //1、根据桥梁加密id获取到，桥梁对象，拿到桥梁id，获取里面对应得用户信息，
        String encryptionId = request.getEncrBridgeId();
        //检查加密的关系id encryptionId 是否在 自己的集合里面，没有就返回
        if (!userInfo.checkBridgeId(encryptionId)) {
            return new ReJson();
        }
        BridgeInfo bridgeInfo = bridgeInfoCache.get(encryptionId).get(); //获取桥梁对象
        if (!bridgeInfo.checkUserId(userId)) { //用户不在关系里面，
            return new ReJson();
        }
        if (StringUtils.isEmpty(bridgeInfo.getUserIds())) {//不存在的用户关系
            return new ReJson();
        }

//        //获取桥梁里面用户对应的发送id
//        Set<String> userIdSet = new HashSet<>();
//        userIdSet.add(userInfo.getUserId()); //添加到自身的集合
//        Set<String> bridgeUserIdSet = new HashSet<>(); //新建一个集合
//        bridgeUserIdSet.addAll(bridgeInfo.getUserIds());//用通道的用户，赋值新对象
//        bridgeUserIdSet.removeAll(userIdSet); //减去自身的集合
//
//        //2、使用长轮询保存对象，获取在线用户，
//        Set<String> onLineSet = new HashSet<>();
//        onLineSet.addAll(multimap.keySet());//在线用户
//        onLineSet.retainAll(bridgeUserIdSet);//在线用户和桥梁集合 的交集，等于 桥梁里在线用户集合
        Set<String> onLineSet = userService.userOnLineSet(userInfo.getUserId(),bridgeInfo,multimap.keySet());

        if (onLineSet.isEmpty()) { //如果在线集合为空那么直接返回
            return new ReJson(200, "", "用户不在线");
        }

        //异步事件保存，聊天记录
        SpringUtil.publishEvent(new ChatRecordEvent(this,request.getContent(),System.currentTimeMillis(),request.getSendTime(),userId,encryptionId));

        //异步事件，处理消息数据
        SpringUtil.publishEvent(new MessageEvent(this, request.getContent(), onLineSet));

        return new ReJson(200, "", "成功接收");
    }

    //用户登陆
    @Override
    @PostMapping("userLogin")
    public ReJson userLogin(String account, String password, String code) throws ExecutionException {
        User u = userService.checkUserLogin(account, password);
//        u.setPassword(null);
//        UserInfo userInfo =loadingCache.get(MD5Utils.md5_16Encrypt(u.getId())).get();

        return new ReJson();
    }

    //模拟发布namespace配置
    @RequestMapping(value = "/publish/{namespace}")
    public Object publishConfig(@PathVariable("namespace") String namespace) {
        if (multimap.containsKey(namespace)) {
            Collection<DeferredResult<Object>> deferredResults = multimap.get(namespace);
            Long time = System.currentTimeMillis();
            //通知所有watch这个namespace变更的长轮训配置变更结果
            List<Map<String, String>> ftpList = new ArrayList<>();
            Map<String, String> map = null;
            for (int i = 0; i < 10; i++) {
                map = new HashMap<String, String>() {
                };
                map.put("test-" + i, "val-" + i);
                map.put("aaa-" + i, "bbb-" + i);
                ftpList.add(map);
            }

            for (DeferredResult<Object> deferredResult : deferredResults) {
                deferredResult.setResult(new ReJson<>(200, ftpList, "传输信息"));
            }
        }
        return "success";
    }

}
